import os
import sys
import re

from configmod import misc
from configmod import generic
import paths as routerconf
from uya.routerconf.configmod.configobj import configobj

variable_equal_regexp = '\s*([^=]+)="?(.*?)"?\s*$'
    
def get_common_options(interface, config):
    """Return common interface options (interface_*)"""
    return generic.get_items(config.items(), "interface_", "interface_")

def get_bridge_options(interface, config):
    """Return bridge options (bridge_*)"""
    return generic.get_items(config.items(), "bridge_", "")

def get_options(interface, config):
    """Return madwifi options (ath_*) for client or AP"""
    output = []
    add = output.append
    parent_interface = "wifi" + re.search("(\d)$", interface).group(0)
            
    # Madwifi client/server
    add(("ath_parent", parent_interface))
    if config["madwifi_type"] == "ap":        
        add(("ath_vaptype", "ap"))
        add(("ath_vapopts", None))
    elif config["madwifi_type"] == "sta":
        add(("ath_vaptype", "sta"))
        add(("ath_vapopts", "nosbeacon"))
    
    # Madwifi security    
    if config["madwifi_security"] == "open":
        add(("ath_security", None))
        add(("wpa_driver", None))
        add(("wpa_conf", None))
        add(("ath_wpa_cfgfile", None))
    elif config["madwifi_security"] == "wep":
        add(("ath_security", "wep"))
        add(("wpa_driver", None))
        add(("wpa_conf", None))
        add(("ath_wpa_cfgfile", None))
        #add(("ath_wep_key1", config["madwifi_ath_wep_key1"]))
    elif config["madwifi_security"] == "wpa2-psk":        
        if config["madwifi_type"] == "sta":
            wpasup = "/etc/wpa_supplicant/wpasupplicant-%s.conf"
            add(("ath_security", None))
            add(("wpa_driver", "madwifi"))
            add(("wpa_conf", wpasup % interface))
            add(("ath_wpa_cfgfile", None))
        elif config["madwifi_type"] == "ap":
            add(("ath_security", "wpa2"))
            add(("wpa_conf", None))
            add(("wpa_driver", None))
            add(("ath_wpa_cfgfile", "/etc/hostapd/hostapd-%s.conf" % interface))

    # Add explicit madwifi ath_* parameters  
    def callback(key, value):
        if key == "madwifi_ath_diversity":
            return int(value)
        elif key == "madwifi_ath_wep_key1" and config["madwifi_security"] != "wep":
            return
        return value  
    output.extend(generic.get_items(config.items(), 
        "madwifi_ath_", "madwifi_", callback))
    
    return output

wpa_supplicant_base = """
ctrl_interface=/var/run/wpa_supplicant-interface
eapol_version=2
ap_scan=1
fast_reauth=1

network={
    ssid=ssid
    scan_ssid=1
    proto=RSN
    key_mgmt=WPA-PSK
    auth_alg=OPEN
    pairwise=CCMP TKIP
    group=CCMP
    priority=10
    psk=psk
}
"""

hostapd_base = """
driver=madwifi
interface=ath1
logger_syslog=1
logger_syslog_level=2
logger_stdout=0
logger_stdout_level=2
debug=0
dump_file=/tmp/hostapd-interface.dump
ctrl_interface=/var/run/hostapd-%s
ctrl_interface_group=0
ssid=ssid
macaddr_acl=0
auth_algs=1
wpa=2
wpa_key_mgmt=WPA-PSK
wpa_pairwise=CCMP
wpa_passphrase=wpa_passphrase
"""

def quote(s, quote_string='"'):
  """Return a quoted a string"""
  return quote_string + s + quote_string

def update_security_files(interface, config):
    """Modify security files for WPA-PSK."""
    if config.get("madwifi_security") != "wpa2-psk":
        return []
    modified_files = []
    if config["madwifi_type"] == "sta":
        wpasup = routerconf.filenames["wpasupplicant"] % interface
        if not os.path.isfile(wpasup):
            content = wpa_supplicant_base.strip().splitlines()
        else:
            content = generic.readlines(wpasup)
        variables = [
            (None, [
                ("ctrl_interface", "/var/run/wpa_supplicant-%s" % interface),
            ]),
            ("network={", [
                ("ssid", quote(config["madwifi_ath_ssid"])),
                ("key_mgmt", "WPA-PSK"),
                ("psk", quote(config["madwifi_wpa_passphrase"])),
            ]),
        ]
        new_content = generic.update_variables_with_sections(
            content, variables,
            section_assignation="^\s*(network=)\s*(.*)\s*$",
            variable_assignation=variable_equal_regexp,
            output_format="%s=%s", section_end="}")
        if generic.overwrite_file(wpasup, new_content):
            modified_files.append(wpasup)            
    elif config["madwifi_type"] == "ap":
        hostapd = routerconf.filenames["hostapd"] % interface
        # idem a lo visto arriba
        if not os.path.isfile(hostapd):
            content = hostapd_base.strip().splitlines()
        else:
            content = generic.readlines(hostapd)
        variables = [
            ("driver", "madwifi"),
            ("interface", interface),
            ("dump_file", "/tmp/hostapd-%s.dump" % interface),
            ("ctrl_interface", "/var/run/hostapd-%s" % interface),
            ("ssid", config["madwifi_ath_ssid"]),
            ("wpa", "2"),
            ("wpa_key_mgmt", "WPA-PSK"),
            ("wpa_passphrase", config["madwifi_wpa_passphrase"]),
        ]
        new_content = generic.update_variables_plain(
                content, variables,
                assignation=variable_equal_regexp, output_format='%s=%s')
        if generic.overwrite_file(hostapd, new_content):
            modified_files.append(hostapd)
        
    return modified_files

def build_interfaces_sections(config, auto_interfaces):
    """Build pairs (section, options) for network interfaces. Return 
    also interfaces enabled and interfaces to be automatically started."""
    config2 = []
    
    for subsection in ["ethernet", "bridging", "wireless"]:
        for interface, pars in config[subsection].items():
            section = ("iface %s %s %s" % 
                (interface, "inet", pars["method"]))
            options = [get_common_options(interface, config[subsection][interface])]
            if subsection == "wireless":
                options.append(get_options(interface, 
                    config[subsection][interface]))
            elif subsection == "bridging":
                options.append(get_bridge_options(interface, 
                    config[subsection][interface]))
            if pars["state"]:
                item = (section, misc.flatten(options))
                auto_interfaces.append(interface)    
            else: 
                item = (section, None)
            if not pars["state"] and interface in auto_interfaces:                
                auto_interfaces.remove(interface)
            config2.append(item)
    # Hack: Normally we don't delete sections we don't know from interfaces
    # file, but we want to assure that only remains brX explicitly configured.
    indexes = [sect[2:] for sect in config["bridging"].sections]
    config2.extend([("iface br%s inet static" % i, None) 
        for i in range(0, 10) if str(i) not in indexes])
    config2.append(("auto %s" % " ".join(misc.uniq(auto_interfaces)), []))
    return config2

interfaces_regexp = re.compile(r""" 
    \s*
    (
        auto|
        allow-\w+\s\w+|
        mapping \w+|
        iface\s\w+
     )\s*(.*)\s*$""", re.VERBOSE)
         

interfaces_variable_regexp = '\s*(\w*) (.*?)\s*$'
         
def update_interfaces_files(config):
    """Update /etc/network/interface."""
    # Disabled ethernet interfaces used in bridges
    for briface in config["bridging"]:
        ports = config["bridging"][briface]["bridge_ports"]
        if not ports:
            continue
        for iface in ports.split():
            if iface in config["ethernet"]:
                config["ethernet"][iface]["state"] = False
    ifacesfile = routerconf.filenames["network-interfaces"]
    lines = generic.readlines(ifacesfile)
    auto_interfaces = misc.flatten([match.group(1).strip().split() for match in 
        misc.itrue(re.match("\s*auto (.*)$", line) for line in lines)])    
    config_processed = build_interfaces_sections(config, auto_interfaces)
    new_content = generic.update_variables_with_sections(
        lines, config_processed,
        interfaces_regexp, 
        interfaces_variable_regexp,  "%s %s", "\t")
    if generic.overwrite_file(ifacesfile, new_content):
        return [ifacesfile] 
    return []
         
def network_interfaces(config, routing_config):
    """Update network configuration (interfaces and security files)""" 
    modified_files = []    
    modified_files.extend(update_interfaces_files(config))                
    for interface, pars in config["wireless"].items():
        modified_files.extend(update_security_files(interface, pars))
    if modified_files:        
        run = [["/etc/init.d/networking", "restart"]]
        if routing_config["routing_mode"] == "dynamic":
            run.append(["/etc/init.d/quagga", "restart"])
    else: run = []        
    return modified_files, run

def get_system_interfaces(procfile="/proc/net/dev"):
    """Return a list with all detected network interfaces"""
    lines = open(procfile).readlines()[2:]
    #return ["eth0", "wifi0", "lo"]
    return map(str.strip, [s.split(":")[0] for s in lines])    

def detect_interfaces(config, groups):
    """Create interface for all system interfaces. For wifiX interfaces
    create the athX logic interfaces."""
    for iface in get_system_interfaces():
        match = re.match("^wifi(\d+)", iface)
        if match:
            iface = "ath%s" % match.group(1)
        for name, section in groups:
            if re.match("^"+name, iface):
                if section not in config:
                    config[section] = {}
                if iface not in config[section]:
                    config[section][iface] = {}
                    config[section][iface]["state"] = False
#                    config[section][iface]["interface_address"] = "192.168.1.1"
                        
def network_interfaces_get(config):
    config["ethernet"].clear()
    groups = [
        ("eth", "ethernet"),
        ("br", "bridging"),
        ("ath", "wireless"),
    ]
    for regexp, group in groups:    
        del config[group]

    def process_option(s, regexp):
        match = re.match(regexp, s)
        if not match:
            return
        key, value = match.groups()
        return key, value
    
    system_interfaces = get_system_interfaces()
    lines = open(routerconf.filenames["network-interfaces"]).readlines()
    ivariables = []
    for section, options in generic.split_sections(lines, interfaces_regexp):
        if section is None:
            continue
        key = section.strip()
        value = filter(bool, [process_option(s, interfaces_variable_regexp) 
            for s in options])
        ivariables.append((key, value)) 

    auto0 = [re.findall("^auto (.*)$", section) for 
        section, options in ivariables]
    auto_interfaces = " ".join(misc.flatten(auto0)).split()
    for regexp, group in groups: 
        if group not in config:
            config[group] = {}

    for section, options in ivariables:
        doptions = dict(options)
        match = re.match("^iface (\w+) (\w+) (\w+)", section)
        if not match:
            continue
        interface, family, method = match.groups()
        # Don't read ethernet or wireless interfaces that have 
        # not been detected on that system
        if interface.startswith("eth") and interface not in system_interfaces:
            continue
        elif interface.startswith("ath") and \
                ("wifi"+interface[3:]) not in system_interfaces:
            continue
            
        group = misc.first(group for (regexp, group) in groups 
            if re.match("^"+regexp, interface))
        if not group:
            continue
        config[group][interface] = {}
        ifconfig = config[group][interface]       
        ifconfig["state"] = interface in auto_interfaces
        ifconfig["method"] = method
        for key, value in options:
            if key in ("address", "netmask"):
                ifconfig["interface_"+key] = value
            elif key in ("ath_parent", "ath_vapopts", "ath_security",
                    "wpa_driver", "wpa_conf", "wpa_driver", "wpa_conf",
                    "ath_wpa_cfgfile"):
                pass
            elif key == "ath_vaptype":
                ifconfig["madwifi_type"] = value or "sta"
            elif key.startswith("ath_"):
                ifconfig["madwifi_"+key] = value                
            elif key.startswith("bridge"):
                ifconfig[key] = value
        ath_security= doptions.get("ath_security")
        if ath_security == "wep":
            ifconfig["madwifi_security"] = "wep"                         
        elif ath_security == "wpa2":
            ifconfig["madwifi_security"] = "wpa2-psk"
        elif (ath_security is None and ifconfig.get("madwifi_type") == "sta" 
                and doptions.get("wpa_driver") == "madwifi" 
                and doptions.get("wpa_conf")):
            ifconfig["madwifi_security"] = "wpa2-psk"

        # /etc/wpa_supplicant
        wpa_conf_file = routerconf.filenames["wpasupplicant"] % interface
        if doptions.get("wpa_conf") and os.path.isfile(wpa_conf_file):
            variables = dict(filter(bool, [process_option(s, variable_equal_regexp) 
                for s in open(wpa_conf_file)]))
            ifconfig["madwifi_wpa_passphrase"] = variables.get("psk")

        # /etc/hostapd
                
        hostapd_file = routerconf.filenames["hostapd"] % interface
        if doptions.get("ath_wpa_cfgfile") and os.path.isfile(hostapd_file):
            variables = dict(filter(bool, [process_option(s, variable_equal_regexp) 
                for s in open(hostapd_file)]))
            ifconfig["madwifi_wpa_passphrase"] = variables.get("wpa_passphrase")
            
    detect_interfaces(config, groups)

    # Order interfaces
    for regexp, group in groups: 
        config[group].sections.sort(key= lambda s: 
            re.match("(\w+)(\d+)", s).groups())
    
